home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac-Source 1994 July
/
Mac-Source_July_1994.iso
/
C and C++
/
Compilers⁄Interps
/
kevoSource
/
blockfiles.c
next >
Wrap
Text File
|
1993-05-18
|
8KB
|
323 lines
/* Kevo -- a prototype-based object-oriented language */
/* (c) Antero Taivalsaari 1991-1993 */
/* Some parts (c) Antero Taivalsaari 1986-1988 */
/* blockfiles.c: block file management internals */
/*
Traditional Forth-style block files.
Not really needed in Kevo, but added for
compatibility (and fun).
Note: in the current implementation block files
are not task-specific, i.e., only one block file
shared by all tasks can be open at each time.
*/
#include "global.h"
/* ----------------------------------------------------------------------- */
#define BUFFERSIZE 1024
#define BUFFERAMOUNT 64
#define DIRTYBIT 0x80000000
/*
Structure for keeping track of blocks that are
currently in memory.
*/
int blockNumbers[BUFFERAMOUNT];
/*
Structure for keeping track of the addresses of buffers.
We allocate space using dynamic allocation. Initially, no
space is allocated, but when 'block' command is used new
buffers are created on the fly. Once all the buffers exist,
they will be "recycled" until deleted by 'empty-buffers'.
*/
char* bufferAddresses[BUFFERAMOUNT];
/*
Variable that tells us how many buffers we have currently
in memory.
*/
int blocksInMemory = 0;
/* Block file pointer */
FILE* blockFile;
/* Private operations: */
/* Check if the block is in memory */
/* Return the position + 1 */
int blockInMemory(thisNumber)
int thisNumber;
{
int i;
for (i = 0; i < blocksInMemory; i++) {
if ((blockNumbers[i] & ~DIRTYBIT) == thisNumber) return(i+1);
}
return(0);
}
/* Scroll the block numbers and buffer addresses downwards */
/* starting from the given position+1 */
void scrollDown(position)
int position;
{
int length = blocksInMemory-position;
memmove(&blockNumbers[position], &blockNumbers[position]+1, length*CELL);
memmove(&bufferAddresses[position], &bufferAddresses[position]+1, length*CELL);
/* Slower implementation:
int i;
for (i = position; i < blocksInMemory; i++) {
blockNumbers[i] = blockNumbers[i+1];
bufferAddresses[i] = bufferAddresses[i+1];
}
*/
}
/* Mark the given block most recently used */
void makeMostRecent(position)
int position;
{
int tempNumber = blockNumbers[position];
char* tempAddress = bufferAddresses[position];
/* If most recently used is referred to again, do nothing */
if (position == blocksInMemory-1) return;
/* Scroll the data structures downwards */
scrollDown(position);
blockNumbers[blocksInMemory-1] = tempNumber;
bufferAddresses[blocksInMemory-1] = tempAddress;
}
void writeOntoDisk(blockNumber, bufferAddress)
int blockNumber;
char* bufferAddress;
{
/* Write the contents of the given buffer to given block onto disk */
if (fseek(blockFile, blockNumber*BUFFERSIZE, SEEK_SET) == 0) {
if (fwrite(bufferAddress, BUFFERSIZE, 1, blockFile) == 1) return;
}
/* Something went wrong with writing */
fprintf(confile, "== File error (cannot write block buffer)."); showTaskID();
if (!supervisor) {
ownPrintf("-- Cannot write block buffer #%d onto disk", blockNumber);
execute((*up)->errorVector);
}
ownLongJmp();
}
/* Discard the least recently addressed buffer,
returning its buffer address for future reuse
*/
char* discardLeastRecent()
{
int LRUblock = blockNumbers[0] & ~DIRTYBIT;
char* bufferAddress = bufferAddresses[0];
/* If the least recent block is dirty, save it onto disk */
if (blockNumbers[0] & DIRTYBIT) writeOntoDisk(LRUblock, bufferAddress);
/* Make space in the block table */
scrollDown(0);
blocksInMemory--;
return(bufferAddress);
}
void pOpenBlockFile()
{
char* fileName = (char*)popData();
FILE* file;
if ((file = fopen(fileName, "rb+")) == NIL) {
fprintf(confile, "== File error (cannot open block file)."); showTaskID();
if (!supervisor) {
ownPrintf("-- Cannot open block file '%s'", fileName);
execute((*up)->errorVector);
}
ownLongJmp();
}
else {
/* If we have an existing block file open, close it now */
if (blockFile) pCloseBlockFile();
blockFile = file;
}
}
void pCloseBlockFile()
{
/* Save and empty the buffers first */
pSaveBuffers();
pEmptyBuffers();
if (blockFile && fclose(blockFile)) {
fprintf(confile, "== File error (cannot close block file)."); showTaskID();
if (!supervisor) {
ownPrintf("-- Cannot close block file");
execute((*up)->errorVector);
}
ownLongJmp();
}
blockFile = NIL;
}
void pBlock()
{
int blockNumber = topData; /* The block to be loaded */
int position;
char* blockAddress;
/* Check if the desired block is already in memory */
if (position = blockInMemory(blockNumber)) {
/* Return the address of that buffer */
topData = (int)bufferAddresses[position-1];
/* Mark that block most recently used */
makeMostRecent(position-1);
yield();
return;
}
/* Otherwise we have to load the block from file */
/* If we already have the maximum number of blocks in memory */
/* we must discard the contents of the least recently used buffer */
if (blocksInMemory >= BUFFERAMOUNT) {
/* Keep the address of the buffer, so we don't have to */
/* worry amount memory management (reuse old buffer) */
blockAddress = discardLeastRecent();
}
/* Otherwise we have to allocate a new buffer */
else {
blockAddress = mymalloc(BUFFERSIZE);
}
/* Load the block */
if (fseek(blockFile, blockNumber*BUFFERSIZE, SEEK_SET) == 0 &&
fread(blockAddress, BUFFERSIZE, 1, blockFile) == 1) {
/* Successful reading: */
/* Update the block file data structure */
blockNumbers[blocksInMemory] = blockNumber;
bufferAddresses[blocksInMemory] = blockAddress;
blocksInMemory++;
/* Return the address to data */
topData = (int)blockAddress;
}
else {
/* Something went wrong with block access */
fprintf(confile, "== File error (cannot access block)."); showTaskID();
if (!supervisor) {
ownPrintf("-- Cannot access block #%d", blockNumber);
execute((*up)->errorVector);
}
ownLongJmp();
}
yield();
}
void pUpdate()
{
/* Raise the dirty bit in the current block */
/* Dirty bit is located in the highest bit of the block number */
blockNumbers[blocksInMemory-1] |= DIRTYBIT;
}
void pDiscard()
{
/* Unset the dirty bit in the current block */
blockNumbers[blocksInMemory-1] &= ~DIRTYBIT;
}
void pSaveBuffers()
{
int i;
/* Write all the dirty buffers onto disk */
/* without affecting their LRU order */
for (i = 0; i < blocksInMemory; i++) {
int thisBlock = blockNumbers[i] & ~DIRTYBIT;
/* If the current block is dirty, it has to be written onto disk */
if (blockNumbers[i] & DIRTYBIT) {
writeOntoDisk(thisBlock, bufferAddresses[i]);
/* Clean the dirty bit */
blockNumbers[i] = thisBlock;
}
}
yield();
}
void pEmptyBuffers()
{
int i;
/* Empty all the buffers and release their storage space */
/* If the buffers have not been saved, changes will be lost */
/* Release the buffers */
for (i = 0; i < blocksInMemory; i++) free(bufferAddresses[i]);
/* Erase the block table */
blocksInMemory = 0;
}
void pMore()
{
int n = popData();
int i;
/* Allocate n more blocks to the end of the file */
/* Think C's standard function 'fseek' does not know how to expand a file */
/* -> do the same manually using 'fputc' */
fseek(blockFile, 0, SEEK_END);
for (i = 0; i < n*BUFFERSIZE; i++) {
/* Fill with zeros */
if (fputc(0, blockFile) == EOF) {
fprintf(confile, "== File error (cannot expand block file)."); showTaskID();
if (!supervisor) {
ownPrintf("-- Unable to add more blocks to block file");
execute((*up)->errorVector);
}
ownLongJmp();
}
}
yield();
}
void pCapacity()
{
fpos_t size;
/* Return the size of the block file (in blocks) */
fseek(blockFile, 0, SEEK_END);
fgetpos(blockFile, &size);
pushData(size/BUFFERSIZE);
}